package com.folderit.apps.rooms.data;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;

import static com.folderit.apps.rooms.data.RoomsContract.ReservationEntry;
import static com.folderit.apps.rooms.data.RoomsContract.RoomEntry;
import static com.folderit.apps.rooms.data.RoomsContract.UserEntry;

public class RoomsProvider extends ContentProvider {

    // The URI Matcher used by this content provider.
    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private RoomsDbHelper mOpenHelper;

    private static final int RESERVATION = 100;
    private static final int RESERVATION_ID = 101;
    private static final int RESERVATION_WITH_ROOM_AND_DATERANGE = 103;

    private static final int ROOM = 200;
    private static final int ROOM_ID = 201;

    private static final int USER = 300;
    private static final int USER_ID = 301;

    private static final SQLiteQueryBuilder sReservationByRoomAndUserQueryBuilder;

    static{
        sReservationByRoomAndUserQueryBuilder = new SQLiteQueryBuilder();
        sReservationByRoomAndUserQueryBuilder.setTables(
                ReservationEntry.TABLE_NAME + " INNER JOIN " + RoomEntry.TABLE_NAME + " ON " +
                        ReservationEntry.TABLE_NAME + "." + ReservationEntry.COLUMN_ROOM_KEY +
                        " = " + RoomEntry.TABLE_NAME + "." + RoomEntry._ID +
                        " INNER JOIN " + UserEntry.TABLE_NAME + " ON " +
                        ReservationEntry.TABLE_NAME + "." + ReservationEntry.COLUMN_USER_KEY +
                        " = " + UserEntry.TABLE_NAME + "." + UserEntry._ID);
    }

    private static final String sReservationWithRoomSelection =
            ReservationEntry.COLUMN_ROOM_KEY + " = ?";

    private static final String sReservationWithRoomAndDateRangeSelection =
            ReservationEntry.COLUMN_ROOM_KEY + " = ? AND " +
                    ReservationEntry.COLUMN_END + " >= ? AND " +
                    ReservationEntry.COLUMN_BEGIN + " <= ?";

    private Cursor getReservationByRoomAndDateRange(
            Uri uri, String[] projection, String sortOrder) {

        String roomId = ReservationEntry.getRoomFromUri(uri);
        String startDate = ReservationEntry.getStartDateFromUri(uri);
        String endDate = ReservationEntry.getEndDateFromUri(uri);

        return sReservationByRoomAndUserQueryBuilder.query(mOpenHelper.getReadableDatabase(),
                projection,
                sReservationWithRoomAndDateRangeSelection,
                new String[]{roomId, startDate, endDate},
                null,
                null,
                sortOrder
        );
    }

    private Cursor getReservationById (
            Uri uri, String[] projection, String sortOrder) {

        long id = ContentUris.parseId(uri);

        return sReservationByRoomAndUserQueryBuilder.query( // Tables (Join)
                mOpenHelper.getReadableDatabase(), // Database
                projection, // Selected columns
                ReservationEntry.TABLE_NAME + "." + ReservationEntry._ID + " = ?", // Where
                new String[]{ String.valueOf(id) }, // Where parameters
                null, // group by
                null, // having
                sortOrder
        );
    }

    private Cursor getReservations(String selection, String[] selectionArgs,
                                   String[] projection, String sortOrder) {

        return sReservationByRoomAndUserQueryBuilder.query( // Tables (Join)
                mOpenHelper.getReadableDatabase(), // Database
                projection, // Selected columns
                selection, // Where
                selectionArgs, // Where parameters
                null, // group by
                null, // having
                sortOrder
        );
    }

    private static UriMatcher buildUriMatcher() {
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = RoomsContract.CONTENT_AUTHORITY;

        // For each type of URI you want to add, create a corresponding code.
        // # = number, * = string

        // reservation
        matcher.addURI(authority, RoomsContract.PATH_RESERVATION, RESERVATION);
        // reservation/1
        matcher.addURI(authority, RoomsContract.PATH_RESERVATION + "/#", RESERVATION_ID);
        // reservation/1/20140823/20140825
        matcher.addURI(authority, RoomsContract.PATH_RESERVATION + "/#/*/*", RESERVATION_WITH_ROOM_AND_DATERANGE);

        // room
        matcher.addURI(authority, RoomsContract.PATH_ROOM, ROOM);
        // room/1
        matcher.addURI(authority, RoomsContract.PATH_ROOM + "/#", ROOM_ID);

        // user
        matcher.addURI(authority, RoomsContract.PATH_USER, USER);
        // user/1
        matcher.addURI(authority, RoomsContract.PATH_USER + "/#", USER_ID);

        return matcher;
    }

    @Override
    public boolean onCreate() {
        mOpenHelper = new RoomsDbHelper(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {
        // Here's the switch statement that, given a URI, will determine what kind of request it is,
        // and query the database accordingly.
        Cursor retCursor;
        switch (sUriMatcher.match(uri)) {
            // "reservation/#/*/*"
            case RESERVATION_WITH_ROOM_AND_DATERANGE:
            {
                retCursor = getReservationByRoomAndDateRange(uri, projection, sortOrder);
                break;
            }
            // "reservation/#"
            case RESERVATION_ID: {
                retCursor = getReservationById(uri, projection, sortOrder);
                break;
            }
            // "reservation"
            case RESERVATION: {
                retCursor = getReservations(selection, selectionArgs, projection, sortOrder);
                /*retCursor = mOpenHelper.getReadableDatabase().query(
                        ReservationEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );*/
                break;
            }
            // "room/#"
            case ROOM_ID: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        RoomEntry.TABLE_NAME,
                        projection,
                        RoomEntry._ID + " = '" + ContentUris.parseId(uri) + "'",
                        null,
                        null,
                        null,
                        sortOrder
                );
                break;
            }
            // "room"
            case ROOM: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        RoomEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );
                break;
            }
            // "user/#"
            case USER_ID: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        UserEntry.TABLE_NAME,
                        projection,
                        UserEntry._ID + " = '" + ContentUris.parseId(uri) + "'",
                        null,
                        null,
                        null,
                        sortOrder
                );
                break;
            }
            // "user"
            case USER: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        UserEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );
                break;
            }

            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        retCursor.setNotificationUri(getContext().getContentResolver(), uri);
        return retCursor;
    }

    @Override
    public String getType(Uri uri) {

        // Use the Uri Matcher to determine what kind of URI this is.
        final int match = sUriMatcher.match(uri);

        switch (match) {
            case RESERVATION_WITH_ROOM_AND_DATERANGE:
                return ReservationEntry.CONTENT_TYPE;
            case RESERVATION_ID:
                return ReservationEntry.CONTENT_ITEM_TYPE;
            case RESERVATION :
                return ReservationEntry.CONTENT_TYPE;
            case ROOM_ID:
                return RoomEntry.CONTENT_ITEM_TYPE;
            case ROOM:
                return RoomEntry.CONTENT_TYPE;
            case USER_ID:
                return UserEntry.CONTENT_ITEM_TYPE;
            case USER:
                return UserEntry.CONTENT_TYPE;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        Uri returnUri;

        switch (match) {
            case RESERVATION: {
                long _id = db.insert(ReservationEntry.TABLE_NAME, null, values);
                if ( _id > 0 )
                    returnUri = ReservationEntry.buildReservationUri(_id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            case ROOM: {
                long _id = db.insert(RoomEntry.TABLE_NAME, null, values);
                if ( _id > 0 )
                    returnUri = RoomEntry.buildRoomUri(_id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            case USER: {
                long _id = db.insert(UserEntry.TABLE_NAME, null, values);
                if ( _id > 0 )
                    returnUri = UserEntry.buildUserUri(_id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return returnUri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        int rowsDeleted;
        switch (match) {
            case RESERVATION:
                rowsDeleted = db.delete(
                        ReservationEntry.TABLE_NAME, selection, selectionArgs);
                break;
            case ROOM:
                rowsDeleted = db.delete(
                        RoomEntry.TABLE_NAME, selection, selectionArgs);
                break;
            case USER:
                rowsDeleted = db.delete(
                        UserEntry.TABLE_NAME, selection, selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        // Because a null deletes all rows
        if (selection == null || rowsDeleted != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsDeleted;
    }

    @Override
    public int update(
            Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        int rowsUpdated;

        switch (match) {
            case RESERVATION:
                rowsUpdated = db.update(ReservationEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            case ROOM:
                rowsUpdated = db.update(RoomEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            case USER:
                rowsUpdated = db.update(UserEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        if (rowsUpdated != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsUpdated;
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        final int match = sUriMatcher.match(uri);
        switch (match) {
            case RESERVATION:
                db.beginTransaction();
                int returnCount = 0;
                try {
                    for (ContentValues value : values) {
                        long _id = db.insert(ReservationEntry.TABLE_NAME, null, value);
                        if (_id != -1) {
                            returnCount++;
                        }
                    }
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
                getContext().getContentResolver().notifyChange(uri, null);
                return returnCount;
            default:
                return super.bulkInsert(uri, values);
        }
    }
}
